package com.learn.io.nio;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author wangxing
 * @version 2020/7/29 16:00 Administrator
 */
public class NIOServer {
    private final static Logger LOGGER = LoggerFactory.getLogger(NIOServer.class);
    public static final int PORT = 9000;
//    public static ExecutorService pool = Executors.newFixedThreadPool(10);
    public static void main(String[] args) throws IOException {
        Selector selector = initServerSocketChannel(PORT);
        select(selector);
    }

    private static Selector initServerSocketChannel(int port) throws IOException {
//        创建一个在本机端口监听服务Socket的通道
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
//        设置通道为非阻塞的
        socketChannel.configureBlocking(false);
//        绑定设置监听的端口
        socketChannel.socket().bind(new InetSocketAddress(port));
//        创建一个选择器
        Selector selector = Selector.open();
//        注册一个可以接受请求的事件到选择器中
        socketChannel.register(selector, SelectionKey.OP_ACCEPT);
        return selector;
    }
    private static void select(Selector selector) throws IOException {
        while(true){
            LOGGER.info("等待事件发生");
//            轮询监听channel中的key,select是阻塞的
            int select = selector.select();
            LOGGER.info("时间连接时间:{}", LocalDateTime.now().toString());
//            获取监听到的数据
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
//            对监听到的事件进行处理
            while(it.hasNext()){
                SelectionKey key = it.next();
                it.remove();
                handle(key);
//                pool.submit(new Thread(()->{
//                    try {
//                    } catch (IOException e) {
//                        e.printStackTrace();
//                    }
//                }));
            }
        }
    }
    private static void handle(SelectionKey key) throws IOException {
        if(key.isAcceptable()){
            acceptable(key);
        }else if(key.isReadable()){
            readable(key);
        }else if(key.isWritable()){
            writable(key);
        }
    }
    /**
     * 连接
     * @param key
     * @throws IOException
     */
    private static void acceptable(SelectionKey key) throws IOException {
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
//        处理完请求不会等待客户端发送数据,accept为阻塞方法,但是这个方法会直接执行,而无需等待事件到来,由于只要进入连接就表示已经有连接过来,不需要阻塞等待
        SocketChannel socketChannel = serverSocketChannel.accept();
        LOGGER.info("有连接接入,接入IP为{},接入时间为{}",socketChannel.getRemoteAddress(),LocalDateTime.now().toString());
//        设置为非阻塞
        socketChannel.configureBlocking(false);
//        监听读取事件,注册到选择器中
        socketChannel.register(key.selector(), SelectionKey.OP_READ);
    }
    private static void writable(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        LOGGER.info("有写入事件发生,IP为{},时间为{}",socketChannel.getRemoteAddress(),LocalDateTime.now().toString());
        ByteBuffer buffer = ByteBuffer.wrap("write".getBytes());
        socketChannel.write(buffer);
        key.interestOps(SelectionKey.OP_READ);
    }

    /**
     * 读事件处理
     * @param key
     * @throws IOException
     */
    private static void readable(SelectionKey key) {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int len=0;
        try {
            LOGGER.info("有读取事件发生,IP为{},时间为{}",socketChannel.getRemoteAddress(),LocalDateTime.now().toString());
            while ((len = socketChannel.read(buffer)) > 0) {
                LOGGER.info("读取到数据为:{}", new String(buffer.array(), 0, len));
            }
            byte[] message = "i am nio server".getBytes();
            ByteBuffer buffer1 = ByteBuffer.wrap(message);
            socketChannel.write(buffer1);

//        设置当前key为读和写
            key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
        }catch (IOException e){
            e.printStackTrace();
        }
    }


}
